package goiot;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class GoiotHttp {
	
	public static final String X_ACCESS_TOKEN = "x-access-token";

	static Logger logger = Logger.getGlobal();

	public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

	private static OkHttpClient client;

	private static String host = "http://localhost:8088";

	private static ObjectMapper objectMapper;

	private static String token;
	
	private GoiotHttp() {}
	
	static {
		client = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
		objectMapper = new ObjectMapper();
		objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		objectMapper.setDateFormat(new MySimpleDateFormat());
	}

	public static <T> T get(String url, TypeReference<T> ref) {
		return exec(url, "get", null, ref);
	}

	public static <T> T post(String url, TypeReference<T> ref) {
		return exec(url, "post", "", ref);
	}

	public static <T> T post(String url, Object data, TypeReference<T> ref) {
		return exec(url, "post", data, ref);
	}

	public static <T> T put(String url, Object data, TypeReference<T> ref) {
		return exec(url, "put", data, ref);
	}

	public static <T> T delete(String url, Object data, TypeReference<T> ref) {
		return exec(url, "delete", data, ref);
	}

	public static <T> T exec(String url, String method, Object data, TypeReference<T> ref) {
		if (client == null) {
			throw new IllegalStateException("init first");
		}
		url = host + url;
		RequestBody body = null;
		if (data != null && !"get".equalsIgnoreCase(method)) {
			if (data instanceof String) {
				body = RequestBody.create((String) data, JSON);
			} else {
				body = RequestBody.create(toJSON(data), JSON);
			}
		}
		okhttp3.Request.Builder builder = new Request.Builder().url(url);
		if ("get".equalsIgnoreCase(method)) {
			builder = builder.get();
		} else if ("post".equalsIgnoreCase(method)) {
			builder = builder.post(body);
		} else if ("put".equalsIgnoreCase(method)) {
			builder = builder.put(body);
		} else if ("delete".equalsIgnoreCase(method)) {
			builder = builder.delete(body);
		} else {
			throw new IllegalArgumentException("method [" + method + "] nonsupport");
		}
		if (null != token) {
			builder.addHeader(X_ACCESS_TOKEN, token);
		}
		Request request = builder.build();
		try (Response response = client.newCall(request).execute()) {
			String respBody = response.body().string();
			if (logger.isLoggable(Level.FINE)) {
				logger.log(Level.FINE, respBody);
			}
			if (!response.isSuccessful()) {
				String msg = "response error: " + response.code() + ", message: " + response.message() + ", url: " + request.url();
				logger.log(Level.INFO, msg);
			}
			if (respBody == null || "".equals(respBody)) {
				return null;
			}
			return objectMapper.readValue(respBody, ref);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public static String toJSON(Object data) {
		try {
			return objectMapper.writeValueAsString(data);
		} catch (JsonProcessingException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public static <T> T parseJSON(String json, TypeReference<T> ref) {
		try {
			return objectMapper.readValue(json.getBytes(), ref);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public static String getHost() {
		return host;
	}

	public static void setHost(String host) {
		GoiotHttp.host = host;
	}

	public static OkHttpClient getClient() {
		return client;
	}

	public static void setClient(OkHttpClient client) {
		GoiotHttp.client = client;
	}

	public static ObjectMapper getObjectMapper() {
		return objectMapper;
	}

	public static void setObjectMapper(ObjectMapper objectMapper) {
		GoiotHttp.objectMapper = objectMapper;
	}

	/** token */
	public static String getToken() {
		return token;
	}
	/** 设置token */
	public static void setToken(String token) {
		GoiotHttp.token = token;
	}
}
